package id;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;

public class ZeroKnowledgeProof {

    public static ECNamedCurveParameterSpec parameters = ECNamedCurveTable.getParameterSpec("secp256r1");

    private Prover prover;
    private Verifier verifier;

    public ZeroKnowledgeProof(String sectorID, Card card) {
        ECPoint sectorPublicKey = Hash.hash(sectorID, parameters);
        prover = new Prover(sectorID, sectorPublicKey, card);
        verifier = new Verifier(sectorPublicKey);
    }

    public boolean proof() {
        ECPoint A = prover.generateA();
        BigInteger c = verifier.generateC(A, prover.getPublicKey());
        BigInteger m = prover.computeM(c);
        boolean b = verifier.verifyProof(m);

        return b;
    }
}

class Prover {
    private Random random;

    private String sectorID;
    private ECPoint sectorPublicKey;

    private Card card;

    private BigInteger r;
    private ECPoint A;

    Prover(String sectorID, ECPoint sectorPublicKey, Card card) {
        random = new SecureRandom();
        this.sectorID = sectorID;
        this.sectorPublicKey = sectorPublicKey;
        this.card = card;
    }

    ECPoint getPublicKey() {
        return card.getSectorKey(sectorID);
    }

    ECPoint generateA() {
        r = new BigInteger(256, random);
        A = sectorPublicKey.multiply(r);

        return A;
    }

    BigInteger computeM(BigInteger c) {
        return card.computeM(r, c);
    }
}

class Verifier {
    private Random random;

    private ECPoint sectorPublicKey;
    private ECPoint proversPublicKey;

    private ECPoint A;
    private BigInteger c;

    Verifier(ECPoint sectorPublicKey) {
        random = new SecureRandom();
        this.sectorPublicKey = sectorPublicKey;
    }

    BigInteger generateC(ECPoint A, ECPoint publicKey) {
        this.A = A;
        this.proversPublicKey = publicKey;
        c = new BigInteger(256, random);

        return c;
    }

    boolean verifyProof(BigInteger m) {
        ECPoint P = sectorPublicKey.multiply(m).subtract(proversPublicKey.multiply(c));

        return P.equals(A);
    }
}
